////////////////////////////////////////////////////////////////////////////////

#include "server_engine.h"

////////////////////////////////////////////////////////////////////////////////

#include <cmath>
#include <iostream>
#include <boost/bind.hpp>

#include "server.h"
#include "network_packets.h"

////////////////////////////////////////////////////////////////////////////////


ServerEngine::ServerEngine( boost::asio::io_service& _io_service )
: m_io_service(_io_service),
  m_Timer(m_io_service)
{
}


////////////////////////////////////////////////////////////////////////////////


ServerEngine::~ServerEngine()
{
}


////////////////////////////////////////////////////////////////////////////////


void ServerEngine::Start( Server* _server )
{
    // Server interface
    m_Server = _server;

    // Run engine
    boost::system::error_code error;
    RunEngineLoop(error);
}


////////////////////////////////////////////////////////////////////////////////


void ServerEngine::ProcessLogic()
{
    ProcessCollisions();
    ProcessMovement();
    ProcessShooting();
}


////////////////////////////////////////////////////////////////////////////////


void ServerEngine::ProcessCollisions()
{
    //Not implemented yet
}


////////////////////////////////////////////////////////////////////////////////


void ServerEngine::ProcessMovement()
{
    Players& players = m_Battlefield.GetPlayers();
    for (Players::iterator it = players.begin(); it != players.end(); ++it)
    {
        // Player
        Player& player = it->second;

        // Set players velocity
        player.CalculateVelocity();
        
        // Move player
        player.Move();
    }

    Bullets& bullets = m_Battlefield.GetBullets();
    Bullets::iterator it = bullets.begin();
    while ( it != bullets.end() )
    {
        // Player
        Bullet& bullet = *it;

        if ( bullet.IsDestroyed() )
        {
            it = bullets.erase(it);
            continue;
        }

        // Move bullet
        bullet.Move();
        ++it;
    }

}


////////////////////////////////////////////////////////////////////////////////


void ServerEngine::ProcessShooting()
{
    Players& players = m_Battlefield.GetPlayers();
    for (Players::iterator it = players.begin(); it != players.end(); ++it)
    {
        // Player
        Player& player = it->second;

        // Get bullet if the player shoots
        Bullet* bullet = player.ProcessShooting();

        if ( bullet )
        {
            Bullets& bullets = m_Battlefield.GetBullets();
            bullets.push_back(*bullet);
            delete bullet;
        }
    }
}


////////////////////////////////////////////////////////////////////////////////


void ServerEngine::Receive( const ClientPacket& _packet )
{
    Players& players = m_Battlefield.GetPlayers();

    // Find player key flags
    Players::iterator it = players.find(_packet.m_ClientID);

    if ( it != players.end() )
    {
        // Player found
        Player& player = it->second;

        player.SetKeyFlags(_packet.m_KeyFlags);
        player.SetAimPosition(_packet.m_MousePosition);
    }
    else
    {
        // New player!
        std::pair<Players::iterator, bool> res;
        res = players.insert( std::make_pair( _packet.m_ClientID, Player() ) );

        Player& player = res.first->second;
        player.SetKeyFlags(_packet.m_KeyFlags);
        player.SetAimPosition(_packet.m_MousePosition);
    }

}


////////////////////////////////////////////////////////////////////////////////


void ServerEngine::FetchServerPacket( ServerPacket& _packet )
{
    // Creation time
    _packet.m_CreationTime = clock();

    STM::Position doublePos;
    STM::ScrPosition pos;

    Players& players = m_Battlefield.GetPlayers();
    size_t playersSize = players.size();

    for (Players::const_iterator it = players.begin(); it != players.end(); ++it)
    {
        doublePos = it->second.GetPosition();
        pos.first = static_cast<short>(doublePos.first);
        pos.second = static_cast<short>(doublePos.second);

        _packet.m_Players.push_back(pos);
    }

    const Bullets& bullets = m_Battlefield.GetBullets();

    for (Bullets::const_iterator it = bullets.begin(); it != bullets.end(); ++it)
    {
        doublePos = it->GetPosition();
        pos.first = static_cast<short>(doublePos.first);
        pos.second = static_cast<short>(doublePos.second);

        _packet.m_Bullets.push_back(pos);
    }
}


////////////////////////////////////////////////////////////////////////////////


void ServerEngine::RunEngineLoop( const boost::system::error_code& _error )
{
    if (!_error)
    {
        // Process engine logic
        ProcessLogic();

        // Fetch server packet
        ServerPacket packet;
        FetchServerPacket(packet);

        static ServerPacket m_PrevPacket = packet;
        
        if (m_PrevPacket != packet)
        {
            m_PrevPacket = packet;
            // Send it to clients
            m_Server->Send(packet);
        }

        // Start from begin
        m_Timer.expires_from_now( boost::posix_time::milliseconds(WAIT_TIME) );
        m_Timer.async_wait(boost::bind( &ServerEngine::RunEngineLoop,
                                        this,
                                        boost::asio::placeholders::error ) );
    }
}


////////////////////////////////////////////////////////////////////////////////
